<?php
class CryptTool {

    public function get_random_salt ($required_salt_len) {
        $raw_length   = (int) ($required_salt_len * 3 / 4 + 1);   # base64 encoding creates 4 bytes encoded for every 3 bytes raw
        $random_bytes = $this->get_random_bytes($raw_length);
        $salt         = $this->base64_encode_salt($random_bytes, $required_salt_len);
        return($salt);
    }

    public function base64_encode_salt ($buffer, $max_length = 0) {
        $encoded_str = str_replace('+', '.', base64_encode($buffer));  # base64 encoding varies. for cryptographic salt, we need the '+' digit to be a '.'
        if ($max_length && strlen($encoded_str) > $max_length)
            $encoded_str = substr($encoded_str, 0, $max_length);
        return($encoded_str);
    }

    # There are 3 cryptographically strong sources for pseudorandom numbers (CSPRNG's)
    #     mcrypt_create_iv
    #     openssl_random_pseudo_bytes
    #     /dev/urandom
    # If none of those work, use mt_rand(). It's the best we've got.
    public function get_random_bytes ($raw_length) {
        $buffer = '';
        $buffer_valid = false;
        if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
            $buffer = mcrypt_create_iv($raw_length, MCRYPT_DEV_URANDOM);
            if ($buffer) {
                $buffer_valid = true;
            }
        }
        if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
            $buffer = openssl_random_pseudo_bytes($raw_length);
            if ($buffer) {
                $buffer_valid = true;
            }
        }
        if (!$buffer_valid && is_readable('/dev/urandom')) {
            $f = fopen('/dev/urandom', 'r');
            $read = strlen($buffer);
            while ($read < $raw_length) {
                $buffer .= fread($f, $raw_length - $read);
                $read = strlen($buffer);
            }
            fclose($f);
            if ($read >= $raw_length) {
                $buffer_valid = true;
            }
        }
        if (!$buffer_valid || strlen($buffer) < $raw_length) {
            $bl = strlen($buffer);
            for ($i = 0; $i < $raw_length; $i++) {
                if ($i < $bl) {
                    $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
                } else {
                    $buffer .= chr(mt_rand(0, 255));
                }
            }
        }
        return($buffer);
    }
}

